#pragma once

#include <iostream>
#include <string>
#include <cstdlib>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "Common.hpp"
#include "log.hpp"
#include "InetAddr.hpp"

using namespace LogModule;

class Socket
{
public: //纯虚方法
    virtual void SocketOrDie() = 0;
    virtual void BindOrDie(uint16_t) = 0;
    virtual void ListenOrDie(int) = 0;
    //virtual std::shared_ptr<Socket> Accept(InetAddr*) = 0;
    virtual int Accept(InetAddr*) = 0;
    virtual void Close() = 0;
    virtual int Recv(std::string&) = 0;
    virtual int Send(const std::string&) = 0;
    virtual int Connect(const std::string&, uint16_t) = 0;
    virtual int Fd() = 0;
public:
    Socket() = default;
    void BuildTcpListenSocket(uint16_t port, int backlog = 16)
    {
        SocketOrDie();
        BindOrDie(port);
        ListenOrDie(backlog);
    }

    void BuildTcpClientListenSocket()
    {
        SocketOrDie();
    }
};

class TcpSocket : public Socket
{
public: //纯虚方法
    virtual void SocketOrDie() override
    {
        _sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if(_sockfd < 0)
        {
            LOG(LogLevel::FATAL) << "socket error";
            exit((int)exit_code::SOCKET_ERR);
        }
        LOG(LogLevel::INFO) << "socket success";
    }

    virtual void BindOrDie(uint16_t port) override
    {
        InetAddr addr(port);
        int n = bind(_sockfd, addr.NetAddrPtr(), addr.AddrLen());
        if(n < 0)
        {
            LOG(LogLevel::FATAL) << "bind error";
            exit((int)exit_code::BIND_ERR);
        }
        LOG(LogLevel::INFO) << "bind success";
    }
    virtual void ListenOrDie(int backlog) override
    {
        int n = listen(_sockfd, backlog);
        if(n < 0)
        {
            LOG(LogLevel::FATAL) << "listen error";
            exit((int)exit_code::LISTEN_ERR);
        }
        LOG(LogLevel::INFO) << "listen success";
    }

    //virtual std::shared_ptr<Socket> Accept(InetAddr* client) override
    virtual int Accept(InetAddr* client) override
    {
        struct sockaddr_in addr;
        socklen_t len = sizeof addr;
        int fd = accept(_sockfd, (sockaddr*)&addr, &len);
        if(fd < 0)
        {
            LOG(LogLevel::WARING) << "accept warning";
            return -1;
        }
        client->SetAddr(addr);
        return fd;
    }

    virtual void Close() override
    {
        if(_sockfd >= 0) close(_sockfd);
    }

    virtual int Recv(std::string& out) override
    {
        char buffer[1024];
        ssize_t len = recv(_sockfd, buffer, sizeof buffer - 1, 0);
        if(len > 0)
        {
            buffer[len] = '\0';
            out += buffer;
        }
        return len;
    }

    virtual int Send(const std::string& in) override
    {
        return send(_sockfd, in.c_str(), in.size(), 0);
    }

    virtual int Connect(const std::string& ip, uint16_t port) override
    {
        InetAddr addr(ip, port);
        return connect(_sockfd, addr.NetAddrPtr(), addr.AddrLen());
    }

    virtual int Fd() override { return _sockfd; } 
public:
    TcpSocket() = default;

    TcpSocket(int fd)
        :_sockfd(fd)
    {}
private:
    int _sockfd = -1;
};